﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace LowLevelGraphics
{
    /// <summary>
    /// How do you modify this if color B is not the midpoint between color A and color C? 
    /// The easiest way is the following. If the parameter (what I call "lambda") is less than 0.5, multiply lambda by two and 
    /// return the interpolated color between color A and color B.
    /// If the parameter is greater than 0.5, multiply lambda by two and subtract one (this maps [0.5, 1] onto [0, 1]) and 
    /// return the interpolated color between color B and color C.
    /// If you don't like the requirement that color B live on the line between color A and color C, 
    /// then you can use exactly the modification that I just described to do a piecewise-linear interpolation between the colors.
    /// Finally, you did not specify if you want to interpolate the so-called alpha value (the 'A' in "ARGB"). 
    /// The above code is easily modified to handle this situation too.
    /// Add one more ComponentSelector defined as color => color.A, 
    /// use InterpolateComponent to interpolate this value and 
    /// use the Color.FromArgb(int, int, int, int) overload of Color.FromArgb.
    /// </summary>
    public class ColorInterpolator
    {
        protected delegate byte ComponentSelector(Color color);
        protected static ComponentSelector _redSelector = color => color.R;
        protected static ComponentSelector _greenSelector = color => color.G;
        protected static ComponentSelector _blueSelector = color => color.B;

        public static Color InterpolateBetween(
            Color endPoint1,
            Color endPoint2,
            double _dLambda)
        {
            if (_dLambda < 0 || _dLambda > 1)
            {
                throw new ArgumentOutOfRangeException("lambda");
            }
            Color color = Color.FromArgb(
                InterpolateComponent(endPoint1, endPoint2, _dLambda, _redSelector),
                InterpolateComponent(endPoint1, endPoint2, _dLambda, _greenSelector),
                InterpolateComponent(endPoint1, endPoint2, _dLambda, _blueSelector)
            );

            return color;
        }

        static byte InterpolateComponent(
            Color endPoint1,
            Color endPoint2,
            double lambda,
            ComponentSelector selector)
        {
            return (byte)(selector(endPoint1)
                + (selector(endPoint2) - selector(endPoint1)) * lambda);
        }

        /// <summary>
        /// Gets the gradients
        /// </summary>
        /// <param name="start"></param>
        /// <param name="end"></param>
        /// <param name="steps"></param>
        /// <returns></returns>
        public static IEnumerable<Color> GetGradients(Color start, Color end, int steps)
        {
            Color stepper = Color.FromArgb((byte)((end.A - start.A) / (steps - 1)),
                                           (byte)((end.R - start.R) / (steps - 1)),
                                           (byte)((end.G - start.G) / (steps - 1)),
                                           (byte)((end.B - start.B) / (steps - 1)));

            for (int i = 0; i < steps; i++)
            {
                yield return Color.FromArgb(start.A + (stepper.A * i),
                                            start.R + (stepper.R * i),
                                            start.G + (stepper.G * i),
                                            start.B + (stepper.B * i));
            }
        }
    }
}
